
'''
chat room client
autor by chenkai 
finished time 2018/6/14

'''
import hashlib
import socket
import threading
import tkinter
import os
import tkinter as tk
from tkinter import *
from tkinter import scrolledtext
from tkinter import messagebox
import tkinter.filedialog
import requests
import time
import pymysql
from hashlib import sha1
##客户端
class App(tk.Tk):
    def __init__(self):

        super().__init__()
        
        self.ip=StringVar()
        self.port=StringVar()
        self.note=StringVar()

        self.tkgui()

    def make_sign(self):
        conn = pymysql.connect(host='localhost', user='root', password='root', db='uesr_log', charset='utf8')

        class Reg(Frame):
            name = 'name'
            pwd = 'pwd'
            conn = pymysql.connect(host='localhost', user='root', password='root', db='uesr_log', charset='utf8')

            def __init__(self, master):
                frame = Frame(master)
                frame.pack()
                self.lab1 = Label(frame, text="关键字:")
                self.lab1.grid(row=0, column=0, sticky=W)
                self.ent1 = Entry(frame)
                self.ent1.grid(row=0, column=1, columnspan=1, sticky=W)
                self.button = Button(frame, text="查找", command=self.find)
                self.button.grid(row=0, column=1, sticky=E)
                self.box = Listbox(frame, width=60, height=30, selectmode="extended")
                self.box.grid(row=1, column=0, columnspan=4, sticky=E)
                self.button = Button(frame, text="删除", command=self.dele)
                self.button.grid(row=0, column=2, columnspan=4, sticky=N)

            def find(self):
                self.box.delete(0, END)
                global str1
                str1 = self.ent1.get()
                if str1 == '':
                    sql = 'select * from msglog ORDER BY  time DESC '
                    cus1 = self.conn.cursor()
                    cus1.execute(sql)
                    self.psw2 = cus1.fetchall()
                    for i, j, k in self.psw2:
                        self.box.insert(END, (j +'  '+ k))
                else:
                    sql = "select * from msglog where msg LIKE \'%" + str1 + "%\' ORDER BY  time DESC"
                    # print(sql)
                    cus1 = self.conn.cursor()
                    cus1.execute(sql)
                    self.psw2 = cus1.fetchall()
                    for i, j, k in self.psw2:
                        self.box.insert(END, (j +'  '+ k))

            def dele(self):
                #if ('self.psw2' in vars()):
                indexs_ = self.box.curselection()
                for i in indexs_:
                    sql = "delete from msglog WHERE id=" + str(self.psw2[i][0])
                    cus1 = self.conn.cursor()
                    cus1.execute(sql)
                    print(self.psw2[i][0])
                # sql="delete"indexs
                return

        root = Tk()
        root.title("聊天记录")
        app = Reg(root)
        root.mainloop()

    def find(self) :
        global  conn_able_,fg_
        if conn_able_  and fg_ :
            ts = threading.Thread(target=self.make_sign)
            ts.start()
            return
        else :
            messagebox.showinfo('提示', '请连接服务器并登陆！')
##界面
    def tkgui(self):
        self.title('客户端')
        self.geometry('600x600')
        self.l_ip=tk.Label(self,text='服务器ip:')
        self.l_ip.grid(row=0,column=0,padx=10,pady=10,sticky=tk.E)
        self.e_ip=tk.Entry(self,textvariable=self.ip)
        self.ip.set('127.0.0.1')

        self.e_ip.grid(row=0,column=1,padx=10,pady=10,sticky=tk.W)
        self.l_port=tk.Label(self,text='服务器端口:')
        self.l_port.grid(row=1,column=0,padx=10,pady=10,sticky=tk.E)
        self.e_port=tk.Entry(self,textvariable=self.port)
        self.port.set('12306')

        self.e_port.grid(row=1,column=1,padx=10,pady=10,sticky=tk.W)
        self.btnst=tk.Button(self,text='登陆',command=self.login)
        self.btnst.grid(row=2,column=1,padx=5,pady=5,sticky=tk.E)
        self.btnst=tk.Button(self,text='连接',command=self.main)
        self.btnst.grid(row=2,column=0,padx=5,pady=5,sticky=tk.E)
        self.btnsp=tk.Button(self,text='断开',command=self.unconnt)
        self.btnsp.grid(row=2,column=1,padx=5,pady=5,sticky=tk.W)

        self.btnsp.configure(state='disable')

        self.find=tk.Button(self,text='查看聊天记录',command=self.find)
        self.find.grid(row=4,column=1,padx=5,pady=5,sticky=tk.E)

        self.find=tk.Button(self,text='上传文件',command=lambda : self.file_op("get "))
        self.find.grid(row=4,column=2,padx=5,pady=5,sticky=tk.E)

        self.find=tk.Button(self,text='下载文件',command=lambda : self.file_op("send "))
        self.find.grid(row=4,column=3,padx=5,pady=5,sticky=tk.E)

        self.sW=80
        self.sH=30
        self.scr=scrolledtext.ScrolledText(self,width=self.sW,height=self.sH,wrap=tk.WORD)
        self.scr.grid(row=3,column=0,columnspan=4)
        self.scr.configure(state='disable')

        self.l_note=tk.Label(self,text='发送内容:')
        self.l_note.grid(row=4,column=0,padx=10,pady=10,sticky=tk.W)
        self.e_note=tk.Entry(self,textvariable=self.note)
        self.e_note.grid(row=4,column=0,padx=10,pady=10,sticky=tk.E)
        self.btnsend=tk.Button(self,text='发送',command=self.sendmg)
        self.btnsend.grid(row=4,column=1,padx=5,pady=5,sticky=tk.W)

    def file_op(self,str_ ):
        client = socket.socket()  # 生成socket连接对象

        ip_port = ("localhost", 6980)  # 地址和端口号

        client.connect(ip_port)  # 连接
        print("服务器已连接")
        if(str_=="send "):
            content = str_+self.e_note.get()
        else :
            content=str_+tkinter.filedialog.askopenfilename(title='选择文件')
        if len(content) == 0:
            return  # 如果传入空字符会阻塞

        if content.startswith("get"):

            client.send(content.encode("utf-8"))  # 传送和接收都是bytes类型

            # 1.先接收长度，建议8192
            server_response = client.recv(1024)
            file_size = int(server_response.decode("utf-8"))

            print("接收到的大小：", file_size)

            # 2.接收文件内容
            client.send("准备好接收".encode("utf-8"))  # 接收确认
            sp=content.split("/")
            filename = "上传/" + sp[len(sp)-1]

            f = open(filename, "wb")
            received_size = 0
            m = hashlib.md5()

            while received_size < file_size:
                size = 0  # 准确接收数据大小，解决粘包
                if file_size - received_size > 1024:  # 多次接收
                    size = 1024
                else:  # 最后一次接收完毕
                    size = file_size - received_size

                data = client.recv(size)  # 多次接收内容，接收大数据
                data_len = len(data)
                received_size += data_len
                print("已接收：", int(received_size / file_size * 100), "%")

                m.update(data)
                f.write(data)

            f.close()

            print("实际接收的大小:", received_size)  # 解码

            # 3.md5值校验
            md5_sever = client.recv(1024).decode("utf-8")
            md5_client = m.hexdigest()
            print("服务器发来的md5:", md5_sever)
            print("接收文件的md5:", md5_client)
            if md5_sever == md5_client:
                print("MD5值校验成功")
            else:
                print("MD5值校验失败")
        elif content.startswith("send"):
            sp=content.split(" ")
            client.send(("send 上传/"+sp[len(sp)-1]).encode("utf-8"))
        client.close()
        return
    def file_save(self):
        server = socket.socket()

        server.bind(("localhost", 6970))  # 绑定监听端口

        server.listen(5)  # 监听

        print("监听开始..")

        while True:
            conn, addr = server.accept()  # 等待连接

            print("conn:", conn, "\naddr:", addr)  # conn连接实例

            while True:
                data = conn.recv(1024)  # 接收
                if not data:  # 客户端已断开
                    print("客户端断开连接")
                    break

                print("收到的命令：", data.decode("utf-8"))
                cmd, filename = data.decode("utf-8").split(" ")
                if cmd == "get":
                    if os.path.isfile(filename):  # 判断文件存在

                        # 1.先发送文件大小，让客户端准备接收
                        size = os.stat(filename).st_size  # 获取文件大小
                        conn.send(str(size).encode("utf-8"))  # 发送数据长度
                        print("发送的大小：", size)

                        # 2.发送文件内容
                        conn.recv(1024)  # 接收确认

                        m = hashlib.md5()
                        f = open(filename, "rb")
                        for line in f:
                            conn.send(line)  # 发送数据
                            m.update(line)
                        f.close()

                        # 3.发送md5值进行校验
                        md5 = m.hexdigest()
                        conn.send(md5.encode("utf-8"))  # 发送md5值
                        print("md5:", md5)

        server.close()
##连接
    def connt(self):
        self.host=self.ip.get()
        self.port1=self.port.get()
        if not self.host:
            tkinter.messagebox.showinfo('提示','请输入IP地址！')
            return
        if not self.port1:
            tkinter.messagebox.showinfo('提示','请输入端口！')
            return
        try:
            self.port1=int(self.port1)
        except:
            #print('无效端口！')
            tkinter.messagebox.showinfo('提示','无效端口！')
            return

        self.s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        
##        self.host='127.0.0.1'
##        self.port=8800
            
        try:
            self.s.connect((self.host,self.port1))
        except:
            #print('连接失败')
            self.scr.configure(state='normal')
            self.scr.insert("insert",'连接失败，服务器没有开启或者IP端口错误！\n')
            self.scr.configure(state='disable')
            self.scr.see(tk.END)
            self.scr.update()
            tkinter.messagebox.showinfo('提示','连接失败！')
            return

        addr=self.s.getsockname()
        self.scr.configure(state='normal')
        self.scr.insert("insert",'yourclient:'+str(addr[1])+'\n')
        self.scr.configure(state='disable')
        self.scr.see(tk.END)
        self.scr.update()
        self.inputs=[self.s]
        global conn_able_
        conn_able_=True
        self.btnst.configure(state='disable')
        self.btnsp.configure(state='normal')        

##断开
    def unconnt(self):
        global fg_
        fg_=False
        global name_now_
        name_now_=''
        global conn_able_
        conn_able_=False
        self.s.close()
        self.scr.configure(state='normal')
        self.scr.insert("insert",'已断开\n')
        self.scr.configure(state='disable')
        self.scr.see(tk.END)
        self.scr.update()
        self.btnst.configure(state='normal')
        self.btnsp.configure(state='disable')
        
    def sendmg(self):
        global fg_
        if False == fg_:
            self.note.set('请登陆后发送')
            return
        try:
            sendmsg=self.note.get()
            if not sendmsg:
                tkinter.messagebox.showinfo('提示','请输入发送内容！')
                return
            global name_now_
            sendmsg=name_now_+' : '+sendmsg
            self.s.sendall(bytes(sendmsg,encoding='utf-8'))
            print('message_error send \n')
        except:
            #print('没有连接')
            tkinter.messagebox.showinfo('提示','没有连接！')
            return
##接收       
    def receiv(self):
        while True:
            #stdinput, stdoutput, stderr = select.select(self.inputs, [], [])
           
            try:
                acceptmsg=str(self.s.recv(1024),encoding='utf-8')
                #print(''.join(acceptmsg))

                if not acceptmsg:
                    #print('服务器断开')
                    self.scr.configure(state='normal')
                    self.scr.insert("insert",'服务器断开\n')
                    self.scr.configure(state='disable')
                    self.scr.see(tk.END)
                    self.scr.update()
                    self.btnst.configure(state='normal')
                    self.btnsp.configure(state='disable')
                    break
                global fg_
                if fg_ :
                    self.scr.configure(state='normal')
                    self.scr.insert("insert",time.ctime()+":\n"+acceptmsg+'\n\n')
                    self.scr.configure(state='disable')
                    self.scr.see(tk.END)
                    self.scr.update()
            except:
                #print('与服务器没有连接')
                tkinter.messagebox.showinfo('提示','没有连接！')
                break
    
    def login(self):
        conn = pymysql.connect(host='localhost',user='root',password='root',db='uesr_log',charset='utf8')
        class Reg (Frame):
            def __init__(self,master):
                frame = Frame(master)  
                frame.pack()
                #self.title('登陆界面')
                self.lab1 = Label(frame,text = "用户名:")
                self.lab1.grid(row = 0,column = 0,sticky = W)  
                self.ent1 = Entry(frame)  
                self.ent1.grid(row = 0,column = 1,sticky = W)  
                self.lab2 = Label(frame,text = "密码:")
                self.lab2.grid(row = 1,column = 0)  
                self.ent2 = Entry(frame,show = "*")  
                self.ent2.grid(row = 1,column = 1,sticky = W)  
                self.button = Button(frame,text = "登陆",command = self.Submit)
                self.button.grid(row = 2,column = 1,sticky = E)  
                self.lab3 = Label(frame,text = "")  
                self.lab3.grid(row = 3,column = 0,sticky = W)  
                self.button2 = Button(frame,text = "退出",command = frame.quit)
                self.button2.grid(row = 3,column = 3,sticky = E)
                self.button = Button(frame,text = "注册",command = self.sign)
                self.button.grid(row = 2,column = 0,sticky = E)

            def sign(self):
                global conn_able_
                if False == conn_able_ :
                    self.lab3["text"] = "请先连接服务器"
                    return
                myname = self.ent1.get()  
                pwd = self.ent2.get()
                if myname =='' or pwd == '' :
                    self.lab3["text"] = "用户名密码不能为空"
                    return
                sql = 'select pwd from userinfo where name=%s'
                cus1 = conn.cursor()
                cus1.execute(sql,myname)
                psw2= cus1.fetchall()
                if psw2 == ():
                    #--加密 pwd2=sha1(pwd.encode('utf-8')).hexdigest()
                    sql = 'insert into userinfo (name,pwd) values (%s,%s)'
                    cus1.execute(sql,(myname,pwd))
                    cus1 = conn.cursor()
                    
                    self.lab3["text"] = "注册成功" 
                else :
                    self.lab3["text"] = "名字已经存在或者不合法的名字"

            def mk_file_log(self,name):
                f = open('./log.txt', 'a', encoding='UTF-8', errors='ignore')
                # f.readlines()
                f.write(time.ctime()+'  登陆名字：'+name+'  '+ "\n" )
                f.close()

            def Submit(self):
                global conn_able_
                if False == conn_able_ :
                    self.lab3["text"] = "请先连接服务器"
                    return
                admin = self.ent1.get()
                pwd = self.ent2.get()
                if admin =='' or pwd == '' :
                    self.lab3["text"] = "用户名密码不能为空"
                    return
                sql = 'select pwd from userinfo where name=%s'
                cus1 = conn.cursor()
                cus1.execute(sql,admin)
                psw= cus1.fetchall()
                if psw == ():
                    self.lab3["text"] ="用户名错误"
                elif psw[0][0] == pwd:
                    self.lab3["text"] ="登陆成功"  
                    global name_now_
                    name_now_=admin
                    global fg_
                    fg_=True
                    self.lab3["text"] = "登陆成功"
                    self.mk_file_log(admin)
                    messagebox.showinfo(title='Python tk',message='登陆成功')
                    root.destroy()
                    return
                else:  
                    self.lab3["text"] = "密码错误"
                self.ent1.delete(0,len(admin))  
                self.ent2.delete(0,len(pwd))  
        root = Tk()  
        root.title("登陆界面")
        app = Reg(root)  
        root.mainloop()
##开始            
    def main(self):
        self.connt()
        ts=threading.Thread(target=self.receiv)

        #ts.setDaemon(True)
        ts.start()

        ts2=threading.Thread(target=self.login)
        ts2.start()

        ts3=threading.Thread(target=self.file_save)
        ts3.start()
        #ts2.setDaemon(True) 
        #ts.join()

if __name__=='__main__':
    fg_ = False
    name_now_ = 'name'
    conn_able_=False
    app=App()
    app.mainloop()
